package tables

import (
	"fmt"
	"log"
	"math"
	"ospf/common"
	"sync"
)

type Node struct {
	ID  common.ID
	Ips []common.ID
}

// MergeIp 往源节点中添加新的节点
func (n *Node) MergeIp(ips []common.ID) (changed bool) {
	for _, ip := range ips {
		occ := false
		for _, ori := range n.Ips {
			if ori == ip {
				occ = true
			}
		}
		if !occ {
			n.Ips = append(n.Ips, ip)
			changed = true
		}
	}
	return
}

type Edge struct {
	From   common.ID
	To     common.ID
	Weight int
}

type LinkStateTable struct {
	Nodes map[common.ID]*Node
	Edges map[common.ID][]Edge
	mu    sync.Mutex
}

// AddNode 添加节点到表中
func (lst *LinkStateTable) AddNode(node *Node) (changed bool) {
	lst.mu.Lock()
	defer lst.mu.Unlock()
	if _, ok := lst.Nodes[node.ID]; !ok {
		lst.Nodes[node.ID] = node
		return true
	}
	return lst.Nodes[node.ID].MergeIp(node.Ips)
}

// AddNodes 添加节点到表中
func (lst *LinkStateTable) AddNodes(node []*Node) (changed bool) {
	for _, n := range node {
		if lst.AddNode(n) {
			changed = true
		}
	}
	return
}

// AddEdge 往链路表中添加边
func (lst *LinkStateTable) AddEdge(from, to common.ID, weight int) (changed bool) {
	lst.mu.Lock()
	defer lst.mu.Unlock()
	changed = true
	for _, edge := range lst.Edges[from] {
		if edge.To == to {
			changed = false
			break
		}
	}
	if changed {
		lst.Edges[from] = append(lst.Edges[from], Edge{From: from, To: to, Weight: weight})
	}
	return
}

// AddEdgesSingleSrc 往链路表中添加单向边
func (lst *LinkStateTable) AddEdgesSingleSrc(from common.ID, to []common.ID, weights []int) (changed bool) {
	lst.mu.Lock()
	defer lst.mu.Unlock()

	for i, t := range to {
		occ := false
		for j, edge := range lst.Edges[from] {
			if edge.To == t {
				// 遇见边存在，但是权重不同的情况
				if edge.Weight != weights[i] {
					// 删除from边集中的t节点
					lst.Edges[from] = append(lst.Edges[from][:j], lst.Edges[from][j+1:]...)
					// 删除t边集中的from节点
					//for k := range lst.Edges[to[i]] {
					//	if lst.Edges[t][k].To == from {
					//		lst.Edges[t] = append(lst.Edges[t][:k], lst.Edges[t][k+1:]...)
					//	}
					//	break
					//}
					break
				}
				occ = true
				break
			}
		}
		if !occ {
			changed = true
			lst.Edges[from] = append(lst.Edges[from], Edge{From: from, To: t, Weight: weights[i]})
			//lst.Edges[t] = append(lst.Edges[from], Edge{From: t, To: from, Weight: weights[i]})
		}
	}
	return
}

// RemoveNode 从边集和节点表中删除节点
func (lst *LinkStateTable) RemoveNode(id common.ID) {
	lst.mu.Lock()
	defer lst.mu.Unlock()
	delete(lst.Nodes, id)
	// 同时删除与该节点相关的边
	for _, edge := range lst.Edges[id] {
		relatedNode := edge.To
		for i := len(lst.Edges[relatedNode]) - 1; i >= 0; i-- {
			if lst.Edges[relatedNode][i].To == id {
				lst.Edges[relatedNode] = append(lst.Edges[relatedNode][:i], lst.Edges[relatedNode][i+1:]...)
				break
			}
		}
	}
	delete(lst.Edges, id)
}

// RemoveEdge 删除链路表中的一条边
func (lst *LinkStateTable) RemoveEdge(from, to common.ID) {
	lst.mu.Lock()
	defer lst.mu.Unlock()
	edges := lst.Edges[from]
	for i := len(edges) - 1; i >= 0; i-- {
		if edges[i].To == to {
			lst.Edges[from] = append(edges[:i], edges[i+1:]...)
		}
	}
}

// Dijkstra 从路由表中读取数据并计算路径，然后存入路由表
//
//goland:noinspection t
func (lst *LinkStateTable) Dijkstra(source common.ID, routingTable *RoutingTable) {
	guard := common.ID{0, 0, 0, 0}

	lst.mu.Lock()
	defer lst.mu.Unlock()

	dist := make(map[common.ID]int)
	vis := make(map[common.ID]bool)
	prev := make(map[common.ID]common.ID)

	for node := range lst.Nodes {
		dist[node] = math.MaxInt32
		prev[node] = guard // 使用 guard 表示没有前驱节点
		vis[node] = false
	}
	dist[source] = 0

	n := len(lst.Nodes)
	for i := 0; i < n; i++ {
		t := guard
		for k, v := range dist {
			if vis[k] {
				continue
			}
			if t == guard || dist[t] > v {
				t = k
			}
		}
		vis[t] = true
		for _, edge := range lst.Edges[t] {
			v := edge.To
			if vis[v] {
				continue
			}
			w := edge.Weight
			if dist[v] > dist[t]+w {
				dist[v] = dist[t] + w
				prev[v] = t
			}
		}
	}

	// 更新路由表
	for node, distance := range dist {
		fmt.Println(node, distance, prev[node])
		if node != source && prev[node] != guard {
			nextHop := prev[node]
			for nextHop != source {
				nextHop = prev[nextHop]
			}
			if nextHop != guard {
				err := routingTable.AddMultipleRoute(lst.Nodes[node].Ips, Route{NextHop: nextHop, Cost: distance})
				if err != nil {
					log.Println(err)
					return
				}
			}
		}
	}
}

func (lst *LinkStateTable) String() string {
	nodes := ""
	for id, node := range lst.Nodes {
		nodes += fmt.Sprintf("id: %v, node: %v\n", id, node)
	}
	edges := ""
	for id, edge := range lst.Edges {
		edges += fmt.Sprintf("id: %v, edge: %v\n", id, edge)
	}
	return fmt.Sprintf("nodes:\n%vedges:\n%v", nodes, edges)
}
